/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hmfs_common.h"

#include <algorithm>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <libgen.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <time.h>
#if defined(__APPLE__)
#include <sys/disk.h>
#endif
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#ifdef HAVE_SCSI_SG_H
#include <scsi/sg.h>
#endif
#ifdef HAVE_LINUX_HDREG_H
#include <linux/hdreg.h>
#endif
#ifdef HAVE_LINUX_LIMITS_H
#include <linux/limits.h>
#endif
#include <unistd.h>
#include <unordered_map>

#ifdef _WIN32
#include "windows.h"
#include "winioctl.h"
#endif

#include "securec.h"
#include "hmfs_data.h"
#include "hmfs_io.h"
#include "hmfs_encoding.h"
#include "hmfs_zoned.h"
#include "hmfs_utils.h"

namespace OHOS::Hmfs {
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif

#if defined(__APPLE__)
#define BLKGETSIZE    DKIOCGETBLOCKCOUNT
#define BLKSSZGET    DKIOCGETBLOCKCOUNT
#endif /* APPLE_DARWIN */


#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
#define BLKGETSIZE    _IO(0x12,96)
#endif

#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
#define BLKGETSIZE64    _IOR(0x12,114, size_t)
#endif

#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
#define BLKSSZGET    _IO(0x12,104)
#endif

/* SCSI command for standard inquiry*/
#define MODELINQUIRY    0x12,0x00,0x00,0x00,0x4A,0x00

#ifndef _WIN32 /* O_BINARY is windows-specific flag */
#define O_BINARY 0
#else
/* On Windows, wchar_t is 8 bit sized and it causes compilation errors. */
#define wchar_t    int
#endif

#ifdef _WIN32
#if (_WIN32_WINNT >= 0x0500)
#define HAVE_GET_FILE_SIZE_EX 1
#endif
#endif

extern hmfs_configuration g_hmfsConfig;
#define _FILE_OFFSET_BITS 64

/* Copied from linux/lib/find_bit.c */
#define BITMAP_FIRST_BYTE_MASK(start) (0xff << ((start) & (BITS_PER_BYTE - 1)))

/*
 * CRC32
 */
#define CRCPOLY_LE 0xedb88320
/*
 * f2fs bit operations
 */
static const int bitsInByte[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};

std::unique_ptr<HmfsCommon> HmfsCommon::instance_ = nullptr;
void HmfsCommon::CreateInstance(CmdConfig &cfgPara)
{
    if (instance_ == nullptr) {
        instance_ = std::make_unique<HmfsCommon>(cfgPara);
    }
}

HmfsCommon& HmfsCommon::GetInstance()
{
    return *instance_.get();
}

int HmfsCommon::LogBase2(uint32_t num)
{
    int ret = 0;
    if (num <= 0 || (num & (num - 1)) != 0) {
        return -1;
    }

    while (num >>= 1) {
        ret++;
    }
    return ret;
}

int HmfsCommon::GetBitsInByte(unsigned char n)
{
    return bitsInByte[n];
}

int HmfsCommon::TestAndSetBitLe(uint32_t nr, uint8_t *addr)
{
    addr += nr >> 3;
    int mask = 1 << ((nr & 0x07));
    int retval = mask & *addr;
    *addr |= mask;
    return retval;
}

int HmfsCommon::TestAndClearBitLe(uint32_t nr, uint8_t *addr)
{
    addr += nr >> 3;
    int mask = 1 << ((nr & 0x07));
    int retval = mask & *addr;
    *addr &= ~mask;
    return retval;
}

int HmfsCommon::TestBitLe(uint32_t nr, const uint8_t *addr)
{
    return ((1 << (nr & 7)) & (addr[nr >> 3]));
}

int HmfsCommon::HmfsTestBit(unsigned int nr, const char *p)
{
    char *addr = (char *)p;
    addr += (nr >> 3);
    int mask = 1 << (7 - (nr & 0x07));
    return (mask & *addr) != 0;
}

int HmfsCommon::HmfsSetBit(unsigned int nr, char *addr)
{
    addr += (nr >> 3);
    int mask = 1 << (7 - (nr & 0x07));
    int ret = mask & *addr;
    *addr |= mask;
    return ret;
}

int HmfsCommon::HmfsClearBit(unsigned int nr, char *addr)
{
    addr += (nr >> 3);
    int mask = 1 << (7 - (nr & 0x07));
    int ret = mask & *addr;
    *addr &= ~mask;
    return ret;
}

uint64_t HmfsCommon::Ffs(uint8_t word)
{
    int num = 0;
    if ((word & 0xf) == 0) {
        num += 4;
        word >>= 4;
    }
    if ((word & 0x3) == 0) {
        num += 2;
        word >>= 2;
    }
    if ((word & 0x1) == 0) {
        num += 1;
    }
    return num;
}

uint64_t HmfsCommon::FindNextBitLeFunc(const uint8_t *addr, uint64_t nbits, uint64_t start, char invert)
{
    if (nbits == 0 || start >= nbits) {
        return nbits;
    }

    uint8_t tmp = addr[start / BITS_PER_BYTE] ^ invert;

    /* Handle 1st word. */
    tmp &= BITMAP_FIRST_BYTE_MASK(start);
    start = round_down(start, BITS_PER_BYTE);

    while (tmp == 0) {
        start += BITS_PER_BYTE;
        if (start >= nbits) {
            return nbits;
        }

        tmp = addr[start / BITS_PER_BYTE] ^ invert;
    }

    return std::min(start + Ffs(tmp), nbits);
}

uint64_t HmfsCommon::FindNextBitLe(const uint8_t *addr, uint64_t size, uint64_t offset)
{
    return FindNextBitLeFunc(addr, size, offset, 0);
}

uint64_t HmfsCommon::FindNextZeroBitL(const uint8_t *addr, uint64_t size, uint64_t offset)
{
    return FindNextBitLeFunc(addr, size, offset, 0xff);
}

bool HmfsCommon::HmfsHasExtraIsize(HmfsInode *inode)
{
    return (inode->iInline & HMFS_EXTRA_ATTR);
}

int HmfsCommon::GetExtraIsize(HmfsInode *inode)
{
    if (HmfsHasExtraIsize(inode)) {
        return LE16_TO_NATIVE(inode->iExtraIsize) / sizeof(uint32_t);
    }
    return 0;
}

int HmfsCommon::GetInlineXattrAddrs(HmfsInode *inode)
{
    if (cfgPara_.features & HMFS_FEATURE_FLEXIBLE_INLINE_XATTR) {
        return LE16_TO_NATIVE(inode->iInlineXattrSize);
    } else if (inode->iInline & HMFS_INLINE_XATTR || inode->iInline & HMFS_INLINE_DENTRY) {
        return DEFAULT_INLINE_XATTR_ADDRS;
    } else {
        return 0;
    }
}

unsigned int HmfsCommon::AddrsPerInode(HmfsInode *i)
{
    unsigned int addrs = CUR_ADDRS_PER_INODE(i) - GetInlineXattrAddrs(i);

    if (!LINUX_S_ISREG(LE16_TO_NATIVE(i->iMode)) ||
        !(LE32_TO_NATIVE(i->iFlags) & HMFS_COMPR_FL)) {
        return addrs;
    }
    return ALIGN_DOWN(addrs, 1 << i->iLogClusterSize);
}

unsigned int HmfsCommon::AddrsPerBlock(HmfsInode *i)
{
    if (!LINUX_S_ISREG(LE16_TO_NATIVE(i->iMode)) ||
        !(LE32_TO_NATIVE(i->iFlags) & HMFS_COMPR_FL)) {
        return DEF_ADDRS_PER_BLOCK;
    }
    return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1 << i->iLogClusterSize);
}

unsigned int HmfsCommon::HmfsMaxFileOffset(HmfsInode *i)
{
    if (!LINUX_S_ISREG(LE16_TO_NATIVE(i->iMode)) ||
        !(LE32_TO_NATIVE(i->iFlags) & HMFS_COMPR_FL)) {
        return LE64_TO_NATIVE(i->iSize);
    }
    return ALIGN_UP(LE64_TO_NATIVE(i->iSize), 1 << i->iLogClusterSize);
}

uint32_t HmfsCommon::HmfsCalCrc32(uint32_t crc, void *buf, int len)
{
    unsigned char *p = (unsigned char *)buf;
    while (len--) {
        crc ^= *p++;
        for (int i = 0; i < 8; i++) {
            crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
        }
    }
    return crc;
}

int HmfsCommon::HmfsCrcValid(uint32_t blkCrc, void *buf, int len)
{
    uint32_t calCrc = HmfsCalCrc32(HMFS_SUPER_MAGIC, buf, len);
    if (calCrc != blkCrc) {
        DBG(0,"CRC validation failed: calCrc = %u, blkCrc = %u buff_size = 0x%x\n",
            calCrc, blkCrc, len);
        return -1;
    }
    return 0;
}

uint32_t HmfsCommon::HmfsInodeChksum(NodeData *node, uint32_t crcSeed)
{
    HmfsInode *ri = &node->i;
    uint32_t ino = node->footer.ino;
    uint32_t gen = ri->iGeneration;
    uint32_t dummyCs = 0;
    unsigned int offset = offsetof(HmfsInode, iInodeChecksum);
    unsigned int csSize = sizeof(dummyCs);

    uint32_t chksum = HmfsCalCrc32(crcSeed, (uint8_t *)&ino, sizeof(ino));
    uint32_t chksumSeed = HmfsCalCrc32(chksum, (uint8_t *)&gen, sizeof(gen));

    chksum = HmfsCalCrc32(chksumSeed, (uint8_t *)ri, offset);
    chksum = HmfsCalCrc32(chksum, (uint8_t *)&dummyCs, csSize);
    offset += csSize;
    chksum = HmfsCalCrc32(chksum, (uint8_t *)ri + offset, HMFS_BLKSIZE - offset);
    return chksum;
}

uint32_t HmfsCommon::HmfsCheckpointChksum(CheckPointData *cp)
{
    unsigned int chksumOffset = LE32_TO_NATIVE(cp->checksumOffset);

    uint32_t chksum = HmfsCalCrc32(HMFS_SUPER_MAGIC, cp, chksumOffset);
    if (chksumOffset < CP_CHKSUM_OFFSET) {
        chksumOffset += sizeof(chksum);
        chksum = HmfsCalCrc32(chksum, (uint8_t *)cp + chksumOffset, HMFS_BLKSIZE - chksumOffset);
    }
    return chksum;
}

unsigned int HmfsCommon::CalcExtraIsize(void)
{
    unsigned int size = offsetof(HmfsInode, iProjid);
    if (cfgPara_.features & HMFS_FEATURE_FLEXIBLE_INLINE_XATTR) {
        size = offsetof(HmfsInode, iProjid);
    }
    if (cfgPara_.features & HMFS_FEATURE_PRJQUOTA) {
        size = offsetof(HmfsInode, iInodeChecksum);
    }
    if (cfgPara_.features & HMFS_FEATURE_INODE_CHKSUM) {
        size = offsetof(HmfsInode, iCrtime);
    }
    if (cfgPara_.features & HMFS_FEATURE_INODE_CRTIME) {
        size = offsetof(HmfsInode, iComprBlocks);
    }
    if (cfgPara_.features & HMFS_FEATURE_COMPRESSION) {
        size = offsetof(HmfsInode, iExtraEnd);
    }
    return size - F2FS_EXTRA_ISIZE_OFFSET;
}

void HmfsCommon::InitQfInode(NodeData *rawNode, uint32_t inodeId, time_t mtime)
{
    SetLeValue(rawNode->footer.nid, inodeId);
    rawNode->footer.ino = rawNode->footer.nid;
    rawNode->footer.cpVer = NATIVE_TO_LE64(1);
    rawNode->i.iMode = NATIVE_TO_LE16(0x8180);
    rawNode->i.iLinks = NATIVE_TO_LE32(1);
    rawNode->i.iUid = NATIVE_TO_LE32(cfgPara_.rootUid);
    rawNode->i.iGid = NATIVE_TO_LE32(cfgPara_.rootGid);

    rawNode->i.iSize = NATIVE_TO_LE64(1024 * 6);
    rawNode->i.iBlocks = NATIVE_TO_LE64(1);

    rawNode->i.iAtime = NATIVE_TO_LE32(mtime);
    rawNode->i.iAtimeNsec = 0;
    rawNode->i.iCtime = NATIVE_TO_LE32(mtime);
    rawNode->i.iCtimeNsec = 0;
    rawNode->i.iMtime = NATIVE_TO_LE32(mtime);
    rawNode->i.iMtimeNsec = 0;
    rawNode->i.iGeneration = 0;
    rawNode->i.iXattrNid = 0;
    rawNode->i.iFlags = FS_IMMUTABLE_FL;
    rawNode->i.iCurrentDepth = NATIVE_TO_LE32(0);
    rawNode->i.iDirLevel = 0;

    if (cfgPara_.features & HMFS_FEATURE_EXTRA_ATTR) {
        rawNode->i.iInline = HMFS_EXTRA_ATTR;
        rawNode->i.iExtraIsize = NATIVE_TO_LE16(CalcExtraIsize());
    }

    if (cfgPara_.features & HMFS_FEATURE_PRJQUOTA) {
        rawNode->i.iProjid = NATIVE_TO_LE32(DEFAULT_PROJECT_ID);
    }

    rawNode->i.iExt.fofs = 0;
    rawNode->i.iExt.blkAddr = 0;
    rawNode->i.iExt.len = 0;
}

int HmfsCommon::WriteInode(NodeData *inode, uint64_t blkaddr, uint32_t crcSeed)
{
    if (cfgPara_.features & HMFS_FEATURE_INODE_CHKSUM) {
        inode->i.iInodeChecksum = NATIVE_TO_LE32(HmfsInodeChksum(inode, crcSeed));
    }
    return HmfsIo::GetInstance().DevWriteBlock(inode, blkaddr);
}

/*
 * try to identify the root device
 */
std::unique_ptr<char[]> HmfsCommon::GetRootdev()
{
#if defined(_WIN32) || defined(WITH_ANDROID)
    return nullptr;
#else
    struct stat sb;
    char buf[PATH_MAX + 1];

    if (stat("/", &sb) == -1) {
        return nullptr;
    }

    snprintf(buf, PATH_MAX, "/sys/dev/block/%u:%u/uevent", major(sb.st_dev), minor(sb.st_dev));

    int fd = open(buf, O_RDONLY);
    if (fd < 0) {
        return nullptr;
    }

    int ret = lseek(fd, (off_t)0, SEEK_END);
    (void)lseek(fd, (off_t)0, SEEK_SET);

    if (ret == -1) {
        close(fd);
        return nullptr;
    }
    auto ueventPtr = std::make_unique<char[]>(ret + 1);
    char *uevent = ueventPtr.get();
    if (uevent == nullptr) {
        close(fd);
        return nullptr;
    }

    uevent[ret] = '\0';

    ret = read(fd, uevent, ret);
    close(fd);

    char *ptr = strstr(uevent, "DEVNAME");
    if (ptr == nullptr) {
        return nullptr;
    }

    ret = sscanf(ptr, "DEVNAME=%s\n", buf);
    if (strlen(buf) == 0) {
        return nullptr;
    }

    ret = strlen(buf) + 5;
    auto rootdevPtr = std::make_unique<char[]>(ret + 1);
    char *rootdev = rootdevPtr.get();
    if (!rootdevPtr) {
        return nullptr;
    }
    rootdev[ret] = '\0';

    snprintf(rootdev, ret + 1, "/dev/%s", buf);
    return rootdevPtr;
#endif
}

/*
 * device information
 */
void HmfsCommon::HmfsInitConfiguration(void)
{
    memset(&g_hmfsConfig, 0, sizeof(hmfs_configuration));
    g_hmfsConfig.ndevs = 1;
    g_hmfsConfig.sectorsPerBlk = DEFAULT_SECTORS_PER_BLK;
    g_hmfsConfig.blksPerSeg = DEFAULT_BLOCKS_PER_SEGMENT;
    g_hmfsConfig.wantedTotalSectors = -1;
    g_hmfsConfig.wantedSectorSize = -1;
#ifndef WITH_ANDROID
    g_hmfsConfig.preserveLimits = 1;
    g_hmfsConfig.noKernelCheck = 1;
#else
    g_hmfsConfig.noKernelCheck = 0;
#endif

    for (int i = 0; i < MAX_DEVICE_COUNT; i++) {
        g_hmfsConfig.devices[i].fd = -1;
        g_hmfsConfig.devices[i].sectorSize = DEFAULT_SECTOR_SIZE;
        g_hmfsConfig.devices[i].endBlkaddr = -1;
        g_hmfsConfig.devices[i].zonedModel = HMFS_ZONED_NONE;
    }

    /* calculated by overprovision ratio */
    g_hmfsConfig.segsPerSec = 1;
    g_hmfsConfig.secsPerZone = 1;
    g_hmfsConfig.segsPerZone = 1;
    g_hmfsConfig.volLabel = nullptr;
    g_hmfsConfig.trim = 1;
    g_hmfsConfig.kd = -1;
    g_hmfsConfig.fixedTime = -1;
    g_hmfsConfig.encoding = 0;
    g_hmfsConfig.encodingFlags = 0;

    /* default root owner */
    g_hmfsConfig.rootUid = getuid();
    g_hmfsConfig.rootGid = getgid();
}

int HmfsCommon::HmfsDevIsWritable(void)
{
    return (g_hmfsConfig.ro == 0) || (g_hmfsConfig.force > 0);
}

#ifdef HAVE_SETMNTENT
int HmfsCommon::IsMounted(const char *mpt, const char *device)
{
    FILE *file = nullptr;
    struct mntent *mnt = nullptr;

    file = setmntent(mpt, "r");
    if (file == nullptr) {
        return 0;
    }

    while ((mnt = getmntent(file)) != nullptr) {
        if (strcmp(device, mnt->mnt_fsname) == 0) {
#ifdef MNTOPT_RO
            if (hasmntopt(mnt, MNTOPT_RO)) {
                g_hmfsConfig.ro = 1;
            }
#endif
            break;
        }
    }
    endmntent(file);
    return mnt ? 1 : 0;
}
#endif

int HmfsCommon::HmfsDevIsUmounted(char *path)
{
#ifdef _WIN32
    return 0;
#else
    int isRootdev = 0;
    int ret = 0;
    auto rootDevName = GetRootdev();
    if (rootDevName) {
        if (strcmp(path, rootDevName.get()) == 0) {
            isRootdev = 1;
        }
    }

    /*
    * try with /proc/mounts fist to detect RDONLY.
    * f2fs_stop_checkpoint makes RO in /proc/mounts while RW in /etc/mtab.
    */
#ifdef __linux__
    ret = IsMounted("/proc/mounts", path);
    if (ret) {
        HMFS_ERROR("Info: Mounted device!\n");
        return -1;
    }
#endif
#if defined(MOUNTED) || defined(_PATH_MOUNTED)
#ifndef MOUNTED
#define MOUNTED _PATH_MOUNTED
#endif
    ret = IsMounted(MOUNTED, path);
    if (ret) {
        HMFS_INFO("Info: Mounted device!\n");
        return -1;
    }
#endif
    /*
    * If we are supposed to operate on the root device, then
    * also check the mounts for '/dev/root', which sometimes
    * functions as an alias for the root device.
    */
    if (isRootdev) {
#ifdef __linux__
        ret = IsMounted("/proc/mounts", "/dev/root");
        if (ret) {
            HMFS_INFO("Info: Mounted device!\n");
            return -1;
        }
#endif
    }
    /*
    * If f2fs is umounted with -l, the process can still use
    * the file system. In this case, we should not format.
    */
    auto statBuffPtr = std::unique_ptr<struct stat>();
    struct stat *statBuff = statBuffPtr.get();
    if (!statBuffPtr) {
        return -1;
    }

    if (stat(path, statBuff) == 0 && S_ISBLK(statBuff->st_mode)) {
        int fd = open(path, O_RDONLY | O_EXCL);
        if (fd >= 0) {
            close(fd);
        } else if (errno == EBUSY) {
            HMFS_ERROR("\tError: In use by the system!\n");
            return -1;
        }
    }
    return ret;
#endif
}

int HmfsCommon::HmfsDevsAreUmounted(void)
{
    for (int i = 0; i < g_hmfsConfig.ndevs; i++) {
        if (HmfsDevIsUmounted((char *)g_hmfsConfig.devices[i].path)) {
            return -1;
        }
    }
    return 0;
}

int32_t HmfsCommon::ReadKernelVersion(const std::string& path, char* buf, size_t len)
{
    if (cfgPara_.sparseMode) {
        return 0;
    }

    std::ifstream file(path, std::ifstream::in);
    if (!file) {
        HMFS_INFO("%s not exist.", path.c_str());
        return -1;
    }

    file.read(buf, len);
    for (size_t i = 0; i < len; i++) {
        if (buf[i] == '\n') {
            buf[i] = 0;
            break;
        }
    }

    return 0;
}

int32_t HmfsCommon::GetKernelVersion(char *version, size_t len)
{
    if (len <= VERSION_TIMESTAMP_LEN) {
        return -1;
    }
    size_t copyLen = len - VERSION_TIMESTAMP_LEN;

    if (ReadKernelVersion("/proc/version", version, len) == 0) {
        return 0;
    }

#ifdef HAVE_SYS_UTSNAME_H
    struct utsname buf;
    if (uname(&buf)) {
        HMFS_ERROR("Failed to get uname.");
        return -1;
    }
#if defined(WITH_KERNEL_VERSION)
    snprintf_s(version, len, copyLen, "%s %s", buf.release, buf.version);
#else
    snprintf_s(version, len, copyLen, "%s", buf.release);
#endif
#endif
    return 0;
}

void HmfsCommon::GetKernelVersion(uint8_t *version)
{
    int i;
    for (i = 0; i < VERSION_STRING_LEN; i++) {
        if (version[i] == '\n') {
            break;
        }
    }
    memset(version + i, 0, VERSION_LEN + 1 - i);
}

void HmfsCommon::GetKernelUnameVersion(uint8_t *version)
{
#ifdef HAVE_SYS_UTSNAME_H
    struct utsname buf;
    memset(version, 0, VERSION_LEN);
    if (uname(&buf)) {
        return;
    }
#if defined(WITH_KERNEL_VERSION)
    snprintf((char *)version, VERSION_STRING_LEN, "%s %s", buf.release, buf.version);
#else
    snprintf((char *)version, VERSION_STRING_LEN, "%s", buf.release);
#endif
#else
    memset(version, 0, VERSION_LEN);
#endif
}

bool HmfsCommon::KernelVersionOver(uint32_t minMajor, uint32_t minMinor)
{
#ifdef HAVE_SYS_UTSNAME_H
    uint32_t major, minor;
    struct utsname uts;

    if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
        return false;
    }

    if (major > minMajor) {
        return true;
    }

    if (major == minMajor && minor >= minMinor) {
        return true;
    }
#endif
    return false;
}


#ifdef __linux__
int HmfsCommon::IsPowerOf2(unsigned long n)
{
    return (n != 0 && ((n & (n - 1)) == 0));
}
#endif


std::vector<std::string> HmfsCommon::SplitStringList(const std::string& srcString, char delimiter)
{
    std::vector<std::string> list;
    std::stringstream strStream(srcString);
    std::string subString;
    while (std::getline(strStream, subString, delimiter)) {
        size_t start = subString.find_first_not_of(" ");
        if (start == std::string::npos) {
            continue;
        }
        size_t end = subString.find_last_not_of(" ");
        list.emplace_back(subString.substr(start, end - start + 1));
    }
    return list;
}

double HmfsCommon::GetBestOverProvision(SuperBlockData* superBlock)
{
    double reserved, ovp, candidate, end, diff, space;
    double maxOvp = 0, maxSpace = 0;
    uint32_t usableSegs = HmfsZoned::GetInstance().HmfsGetUsableSegments(superBlock);

    if (GetLeValue(superBlock->segmentCountInMain) < 256) {
        candidate = 10;
        end = 95;
        diff = 5;
    } else {
        candidate = 0.01;
        end = 10;
        diff = 0.01;
    }

    for (; candidate <= end; candidate += diff) {
        reserved = (2 * (100 / candidate + 1) + 6) *
                round_up(usableSegs, GetLeValue(superBlock->sectionCount));
        ovp = (usableSegs - reserved) * candidate / 100;
        space = usableSegs - reserved - ovp;
        if (maxSpace < space) {
            maxSpace = space;
            maxOvp = candidate;
        }
    }
    return maxOvp;
}


#define DELTA 0x9E3779B9
static void TeaTransform(unsigned int buf[4], unsigned int const in[])
{
    uint32_t sum = 0;
    uint32_t b0 = buf[0], b1 = buf[1];
    uint32_t a = in[0], b = in[1], c = in[2], d = in[3];
    int n = 16;

    do {
        sum += DELTA;
        b0 += ((b1 << 4) + a) ^ (b1 + sum) ^ ((b1 >> 5) + b);
        b1 += ((b0 << 4) + c) ^ (b0 + sum) ^ ((b0 >> 5) + d);
    } while (--n);

    buf[0] += b0;
    buf[1] += b1;
}

static void Str2Hashbuf(const unsigned char *msg, int len, unsigned int *buf, int num)
{
    unsigned pad = (uint32_t)len | ((uint32_t)len << 8);
    pad |= pad << 16;

    unsigned val = pad;
    if (len > num * 4) {
        len = num * 4;
    }

    for (int i = 0; i < len; i++) {
        if ((i % 4) == 0) {
            val = pad;
        }
        val = msg[i] + (val << 8);
        if ((i % 4) == 3) {
            *buf++ = val;
            val = pad;
            num--;
        }
    }
    if (--num >= 0) {
        *buf++ = val;
    }
    while (--num >= 0) {
        *buf++ = pad;
    }
}

static uint32_t HmfsDentryHash(const unsigned char *name, int len)
{
    uint32_t hash;
    uint32_t hmfsHash;
    const unsigned char *p;
    uint32_t in[8];
    uint32_t buf[4];

    /* special hash codes for special dentries */
    if ((len <= 2) && (name[0] == '.') &&
        (name[1] == '.' || name[1] == '\0')) {
        return 0;
    }

    /* Initialize the default seed for the hash checksum functions */
    buf[0] = 0x67452301;
    buf[1] = 0xefcdab89;
    buf[2] = 0x98badcfe;
    buf[3] = 0x10325476;

    p = name;
    while (1) {
        Str2Hashbuf(p, len, in, 4);
        TeaTransform(buf, in);
        p += 16;
        if (len <= 16) {
            break;
        }
        len -= 16;
    }
    hash = buf[0];

    hmfsHash = NATIVE_TO_LE32(hash & ~HMFS_HASH_COL_BIT);
    return hmfsHash;
}

uint32_t HmfsCommon::DentryHash(int encoding, int casefolded, const unsigned char *name, int len)
{
    bool isNeedEncoding = IsNeedEncoding(encoding);
    if (!isNeedEncoding) {
        return HmfsDentryHash(name, len);
    }

    if (len != 0 && casefolded != 0) {
        auto buff = std::make_unique<unsigned char[]>(HMFS_NAME_LEN);
        if (!buff) {
            return -ENOMEM;
        }
        int dlen = CaseFold(name, len, buff.get(), HMFS_NAME_LEN);
        if (dlen <= 0) {
            return HmfsDentryHash(name, len);
        }
        return HmfsDentryHash(buff.get(), dlen);
    }
    return HmfsDentryHash(name, len);
}

}